/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#ifndef _INC_AK_SOCKET_
#define	_INC_AK_SOCKET_

#ifdef __cplusplus
extern "C" {
#endif

#include "ak_types.h"
#include "ak_lock.h"
#include "ak_task.h"

/* ---------------------------------------------------------------------------
** Socket:  OS -> AIO -> SockFD -> Socket Layer EvHandler.
*/

/* Linux */
#if 	_TARGET_OS == _OS_GNU
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>

#define S_SOCKET_ADDR 		struct  sockaddr
#define S_SOCKET_ADDRIN 	struct 	sockaddr_in
#define S_SOCKET_ADDRIN6 	struct 	sockaddr_in6
#define S_SOCKET 			int
#define S_SOCKET_INVALID	-1
#define S_SOCKET_ERROR 		-1
#define S_SOCKET_CLOSE		close
#define S_SOCKET_SOMAXCONN	128

#define S_SOCKET_ADDRUX		struct  sockaddr_un

/* Scatter/Gather Buffer */
typedef struct iovec  	_t_sgbuf;
#define SGBUF_PTR 	iov_base
#define SGBUF_LEN	iov_len 	

/* Set Non-Block */
#define S_SOCK_SET_NONBLOCK(vFd, vFlag) 	\
do{	\
	vFlag = fcntl(vFd, F_GETFL, 0);	\
	fcntl(vFd, F_SETFL, (vFlag)|O_NONBLOCK);\
}while(0)

/* IP TOS */
#define S_SOCK_SET_THROUGHPUT(vFd, vFlag) 	\
do{ \
	vFlag = IPTOS_THROUGHPUT;  \
	setsockopt(vFd, IPPROTO_IP, IP_TOS, (const char*)(&vFlag), sizeof(int));\
}while(0)

#define S_SOCK_SET_LOWDELAY(vFd, vFlag) 	\
do{ \
	vFlag = IPTOS_LOWDELAY;  \
	setsockopt(vFd, IPPROTO_IP, IP_TOS, (const char*)(&vFlag), sizeof(int));\
}while(0)

/* Windows */
#elif 	_TARGET_OS == _OS_WIN

#include <mswsock.h>
#pragma  comment(lib,"ws2_32.lib")

#define S_SOCKET_ADDR 		SOCKADDR
#define S_SOCKET_ADDRIN 	SOCKADDR_IN
#define S_SOCKET			SOCKET
#define S_SOCKET_INVALID	INVALID_SOCKET
#define S_SOCKET_ERROR 		SOCKET_ERROR 
#define S_SOCKET_CLOSE		closesocket
#define S_SOCKET_SOMAXCONN	SOMAXCONN

#define S_SOCKET_GETPEER 	getpeername

/* Scatter/Gather Buffer */
typedef WSABUF 			_t_sgbuf;
#define SGBUF_PTR 	buf
#define SGBUF_LEN	len 	


/* Set Non-Block */
#define S_SOCK_SET_NONBLOCK(vFd, vFlag) 	\
do{	\
	vFlag = 1;	\
	ioctlsocket(vFd, FIONBIO, &(vFlag));\
}while(0)

#endif


/* Network Byte Order */
#define NTOHS(var) 	(var = ntohs(var))
#define NTOHL(var) 	(var = ntohl(var))
#define HTONS(var) 	(var = htons(var))
#define HTONL(var) 	(var = htonl(var))

/* Socket Type */
typedef enum {
	SOCKTYPE_TCP = 0,
	SOCKTYPE_UDP,
	SOCKTYPE_RTP,
	SOCKTYPE_SCTP,
	SOCKTYPE_UNIX,
	SOCKTYPE_RAW,
	SOCKTYPE_NA
}_e_sockType;

/* Socket State */
typedef enum{
	SOCKST_NUL = 0,
	SOCKST_CONN= 1,
	SOCKST_OK  = 2
}_e_sockState;

/* I/O Function */
typedef struct{
	/* Ctrl :  */
	int 			(*	connect)();	
	int 			(*	accept)(); 

	/* Data : */
	int 			(* 	send)();	
	int 			(* 	recv)();

	/* Open/Close */
	int 			(* 	open)();
	int 			(* 	close)();
}_t_sockFunc;

extern sInt32		SockLib_Init(void);
extern void			SockLib_Fini(void);
/* UDP Socket */
extern	S_SOCKET 	SockUdp_Open(unsigned int ip, unsigned short port, int bBind);
extern	S_SOCKET 	SockCreate_Udp(sInt32 bBind, uInt16 port);
extern	sInt32 		SockRd_Udp(S_SOCKET socket, uInt8 * pBuf, uInt32 bLen, uInt32 * pSrcIP, uInt16 * pSrcPort);
extern	sInt32 		SockWr_Udp(S_SOCKET socket, uInt8 * pBuf, uInt32 bLen, uInt32 ipAddr, uInt16 port);

/* Normal Socket */
extern	S_SOCKET 	SockCreate_Type(_e_sockType type, sInt32 bBind, uInt16 port);
extern	sInt32 		SockListen_Tcp(S_SOCKET sock);

/* Host Function */
extern	uInt32 		HostGet_IP(char * sHost);
extern	int 		HostGet_LocalAddr(char * sAddr, uInt32 nSize);

/* Init the Sock-Addr */
#define S_ADDRIN_INIT(vSockAddr, vIP, vPort) \
do{\
	SYS_MEMSET(&(vSockAddr), 0, sizeof(S_SOCKET_ADDRIN)); \
	(vSockAddr).sin_family		= AF_INET;\
	(vSockAddr).sin_port		= htons(vPort);\
	(vSockAddr).sin_addr.s_addr	= htonl(vIP);\
}while(0)

static __inline void 		SockSet_Linger(S_SOCKET sock)
{
	struct linger		vLinger;
	int 				vFlag;

	vFlag = 0x1;  
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,  (const char*)(&vFlag), sizeof(int));
	vLinger.l_onoff 	= 1;
	vLinger.l_linger	= 0;
	setsockopt(sock, SOL_SOCKET, SO_LINGER,		(const char*)(&vLinger), sizeof(struct linger));
}

#define S_SOCK_SET_REUSEADDR(vFd, vFlag) 	\
do{ \
	vFlag = 0x1;  \
	setsockopt(vFd, SOL_SOCKET, SO_REUSEADDR, (const char*)(&vFlag), sizeof(int));\
}while(0)

#define S_SOCK_SET_BROADCAST(vFd, vFlag) 	\
do{ \
	vFlag = 0x1;  \
	setsockopt(vFd, SOL_SOCKET, SO_BROADCAST, (const char*)(&vFlag), sizeof(int));\
}while(0)

/* Buffer */
#define S_SOCK_SET_SNDBUF(vFd, vFlag, vValue) 	\
do{ \
	vFlag = vValue;  \
	setsockopt(vFd, SOL_SOCKET, SO_SNDBUF, (const char*)(&vFlag), sizeof(int));\
}while(0)

#define S_SOCK_SET_RCVBUF(vFd, vFlag, vValue) 	\
do{ \
	vFlag = vValue;  \
	setsockopt(vFd, SOL_SOCKET, SO_RCVBUF, (const char*)(&vFlag), sizeof(int));\
}while(0)

#define S_SOCK_SET_NOCHKSUM(vFd, vFlag) 	\
do{ \
	vFlag = 1;  \
	setsockopt(vFd, SOL_SOCKET, SO_NO_CHECK, (const char*)(&vFlag), sizeof(int));\
}while(0)

/* TCP */
#define S_SOCK_SET_TCPNODELAY(vFd, vFlag) 	\
do{ \
	vFlag = 1;  \
	setsockopt(vFd, IPPROTO_TCP, TCP_NODELAY, (const char*)(&vFlag), sizeof(int));\
}while(0)


/*  ---------------------------------------------------------------------------
**  Network Scatter Gather Buffer
*/
typedef struct{
	_t_sgbuf * 	sgBuf;
	int 		sgCnt;
	int 		offset;
}_t_sgclue;

#define SGBUF_ASSIGN(pSgBuf, pBuf, lBuf) \
do{\
	(pSgBuf)->SGBUF_PTR = (pBuf);\
	(pSgBuf)->SGBUF_LEN = (lBuf);\
}while(0)

#define SGCLUE_INIT(clue, buf, count, off) \
do{\
	(clue)->sgBuf 	= (buf);\
	(clue)->sgCnt 	= (count);\
	(clue)->offset 	= (off);\
}while(0)

static	__inline	long	SgBuf_Size(_t_sgbuf * pSgBuf, int sgCnt){
	int 	i;
	long 	length;
	for(i=0, length=0; i<sgCnt; i++, pSgBuf++){
		if((pSgBuf->SGBUF_PTR == NULL)){
			return -1;
		}
		length += (pSgBuf->SGBUF_LEN);
	}
	return length;
}

extern	long	SgBuf_Copy(_t_sgclue  * sgClue, unsigned char * buf, long len);


/* ---------------------------------------------------------------------------
** Event Select:
*/
#define S_EVOP_TX		0X0100
#define S_EVOP_RX		0X0200
#define S_EVOP_TIMER	0X0800

#define S_EVOP_ERR		0X4000
#define S_EVOP_MISC		0X8000

typedef struct{
	/* Slow Path : Dispatch */
	int (*	cbDisp	)();
	
	/* Fast Path :  Read, Write, Error */
	int (*	cbIn	)();
	int (*	cbOut	)();
	int (*	cbErr	)();
}_t_sockEvCb;

typedef struct {
	_e_sockType 		type;
	_e_sockState 		state;
	/* Handle: Add SCTP Support. Assoc! */
	S_SOCKET 			fd;
	int 				assoc;

	_t_sockFunc 	* 	pSFunc;
	/* Sock Addr */
	#if 0
	union{
		S_SOCKET_ADDRIN inAddr;
		
	#if _TARGET_OS == _OS_GNU
		S_SOCKET_ADDRUX uxAddr;
	#endif
	};
	#endif
	
	/* Call Back & eCase */
	_t_sockEvCb 	* 	pCb;
	unsigned long		eCase;
	
	#if _TARGET_OS == _OS_WIN
	WSAEVENT			event;
	int 				index;
	#endif
}_t_sockEvUser;

/* Linux */
#if 	_TARGET_OS == _OS_GNU
#define S_EVSEL_STACK 	0X100000
#define S_EVSEL_MAX 	0X10000
#define S_EVSEL_TO 		100

typedef int  	_t_sysEvAio; 	/* EPoll Handle */
typedef struct 	_t_sysEvSel{
	_t_sysCS 			lock;
	_t_sysEvAio 		aioHandle;
}_t_sysEvSel;

#define 	S_ECASE_ACPT 	(EPOLLIN |EPOLLET)
#define 	S_ECASE_NORM 	(EPOLLIN |EPOLLOUT| EPOLLRDHUP |EPOLLET)
#define 	S_ECASE_CONN 	(EPOLLIN |EPOLLOUT| EPOLLRDHUP |EPOLLET)

/* ASync Socket Ops */
extern	int 	SockTx_Udp(int 	sockId, unsigned char * pMsg, int msgLen, S_SOCKET_ADDRIN*	pSockAddr);
extern	int 	SockRx_Udp(int 	sockId, _t_cbentry * pCbEntry);
extern	int 	SockRxSel_Udp(int sockId, _t_cbentry* pCbEntry, int timeout);
extern	int 	SockTx_UdpSg(int sockId, _t_sgbuf * sgBuf, int sgCnt, S_SOCKET_ADDRIN * pSockAddr);

extern	int 	SockTx_Tcp(int 	sockId, unsigned char * pMsg, int msgLen, S_SOCKET_ADDRIN*	pSockAddr);
extern	int 	SockRx_Tcp(int 	sockId, _t_cbentry * pCbEntry);
extern	int 	SockRxSel_Tcp(int  sockId, _t_cbentry* pCbEntry, int timeout);

extern	int 	SockTx_Unix(int sockId, unsigned char * pMsg,  int msgLen, S_SOCKET_ADDRUX* pSockAddr);
extern	int 	SockRx_Unix(int sockId, _t_cbentry * pCbEntry);
extern	int 	SockRxSel_Unix(int sockId, _t_cbentry * pCbEntry, int timeout);

/* Windows */
#elif 	_TARGET_OS == _OS_WIN
#define S_EVSEL_STACK 	0X40000
#define S_EVSEL_MAX 	(WSA_MAXIMUM_WAIT_EVENTS - 1)
#define S_EVSEL_TO 		5

typedef HANDLE 	_t_sysEvAio; 	/* Event Select Handle */
typedef struct 	_t_sysEvSel{
	int 			flag;		/* Dirty Flag */
	uInt32 			count;
	WSAEVENT  		event[S_EVSEL_MAX];
	_t_sockEvUser* 	eUser[S_EVSEL_MAX];
	_t_sysCS 		lock;
}_t_sysEvSel;

#define 	S_ECASE_ACPT 	(FD_READ|FD_ACCEPT|FD_CLOSE)
#define 	S_ECASE_NORM 	(FD_READ|FD_WRITE|FD_CLOSE)
#define 	S_ECASE_CONN 	(FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE)

#endif

extern	void 	EvSel_Init(_t_sysEvSel * pEvSel);
extern	void * 	EvSel_Exec(_t_sysEvSel * pEvSel);
extern	sInt32 	EvSel_Add(_t_sysEvSel * pEvSel, _t_sockEvUser * pEvUser);
extern	sInt32 	EvSel_Del(_t_sysEvSel * pEvSel, _t_sockEvUser * pEvUser);
extern	sInt32 	EvSel_Mod(_t_sysEvSel * pEvSel, _t_sockEvUser * pEvUser);



/* ---------------------------------------------------------------------------
** IP Port:
*/
typedef union {
	/* IPV4 */ 
	unsigned long long	b64;
	unsigned int		b32[2];
	unsigned short		b16[4];
	/* Unix */
	char 			* 	path;
}_u_ipport;

#define _IPPORT_INIT(vIPPort) \
	do{\
		(vIPPort)->b16[0] = (unsigned short )(0XCCCC);\
		(vIPPort)->b16[1] = 0;\
		(vIPPort)->b32[1] = (unsigned int	)(0XCCCCCCCC);\
	}while(0)
	
#define _IPPORT_VALID(vIPPort) (((vIPPort)->b16[0] != 0XCCCC) && ((vIPPort)->b32[1] != 0XCCCCCCCC))

#define _IPPORT_GEN(vIPPort, vIP, vPort) 	\
	do{\
		(vIPPort)->b16[0] = (unsigned short )(vPort);\
		(vIPPort)->b16[1] = 0;\
		(vIPPort)->b32[1] = (unsigned int	)(vIP);\
	}while(0)

#define _IPPORT_ASSIGN(vDst, vSrc) 	\
	do{\
		(vDst)->b64 = (vSrc)->b64;\
	}while(0)

#define _IPPORT_PORT(vIPPort) 	((vIPPort)->b16[0])
#define _IPPORT_IP(	vIPPort) 	((vIPPort)->b32[1])



/* Sock Lib Command */
extern	int 	SSock_SetTraceLvl(int lvl);
extern	int 	SSock_GetTraceLvl(void);

#ifdef __cplusplus
}
#endif

#endif

